// Virtual PoleView.cpp : implementation of the CVirtualPoleView class
//

#include "stdafx.h"
#include "math.h"

#include "Virtual Pole.h"
#include "Virtual PoleDoc.h"
#include "Virtual PoleView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//error checking macro
#define errChk(fCall) if (m_iError = (fCall), m_iError <0) {goto Error;} else

// server messages
#define SERVER_MESSAGE_NOTIFICATION      1048 //Custom notification message

/////////////////////////////////////////////////////////////////////////////
// CVirtualPoleView

IMPLEMENT_DYNCREATE(CVirtualPoleView, CView)

BEGIN_MESSAGE_MAP(CVirtualPoleView, CView)
	//{{AFX_MSG_MAP(CVirtualPoleView)
	ON_WM_CREATE()
	ON_COMMAND(ID_VIEW_VIRTUALPOLE, OnViewVirtualPole)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RAW_VIDEO, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(ID_VIEW_VIRTUALPOLE, OnUpdateViewVirtualPole)
	ON_COMMAND(ID_SETTINGS_SETMESURMENTUNITS, OnSettingsSetMesurmentUnits)
	ON_COMMAND(ID_SETTINGS_SETROI, OnSettingsSetROI)
	ON_COMMAND(IDR_SET_THRESHOLDS, OnSettingsSetThresholds)
	ON_COMMAND(ID_SETTINGS_SETVIRTUALPOLE, OnSettingsSetVirtualPole)
	ON_COMMAND(IDR_SET_CAMERA, OnSettingsSetProperties)
	ON_COMMAND(IDR_FILE_CLOSE_VIDEO, OnFileCloseVideo)
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_COMMAND(IDR_SET_ROI, OnToolBarSetRoi)
	ON_COMMAND(IDR_SET_MESURMENTS, OnToolBarSetMesurmentUnits)
	ON_COMMAND(IDR_SET_VP, OnToolBarSetVP)
	ON_COMMAND(ID_VIEW_KEEPASPECTRATIO, OnViewKeepaspectratio)
	ON_UPDATE_COMMAND_UI(ID_VIEW_KEEPASPECTRATIO, OnUpdateViewKeepaspectratio)
	ON_COMMAND(IDR_FILE_EXIT, OnFileExit)
	ON_COMMAND(IDR_ANLYZE_START, OnAnalyzeStart)
	ON_COMMAND(ID_VIEW_FRAMESWHILE, OnViewSourceVideo)
	ON_UPDATE_COMMAND_UI(ID_VIEW_FRAMESWHILE, OnUpdateViewSourceVideo)
	ON_UPDATE_COMMAND_UI(ID_VIEW_ANALYZEDVIDEO, OnUpdateViewAnalyzedVideo)
	ON_COMMAND(ID_VIEW_ANALYZEDVIDEO, OnViewAnalyzedVideo)
	ON_COMMAND(ID_VIEW_VIEWFRAMENUMBERS, OnViewFrameNumbers)
	ON_UPDATE_COMMAND_UI(ID_VIEW_VIEWFRAMENUMBERS, OnUpdateViewFrameNumbers)
	ON_COMMAND(ID_FILE_STARTCAPTUREVIDEOFILE, OnFileStartCaptureVideo)
	ON_UPDATE_COMMAND_UI(IDR_ANLYZE_START, OnUpdateAnlyzeStart)
	ON_UPDATE_COMMAND_UI(IDR_ANALYZE_STOP, OnUpdateAnalyzeStop)
	ON_COMMAND(IDR_ANALYZE_STOP, OnAnalyzeStop)
	ON_UPDATE_COMMAND_UI(ID_FILE_STARTVIDEOSESSION, OnUpdateFileStartVideoSession)
	ON_COMMAND(IDR_LISTEN, OnListen)
	ON_COMMAND(ID_ANALYZE_SAVE, OnAnalyzeSave)
	ON_UPDATE_COMMAND_UI(ID_ANALYZE_SAVE, OnUpdateAnalyzeSave)
	ON_UPDATE_COMMAND_UI(ID_FILE_LISTENONEXTERNALPORT, OnUpdateFileListenOnExternalPort)
	ON_COMMAND(ID_SETTINGS_SETTHRESHOLDS, OnSettingsSetThresholds)
	ON_COMMAND(ID_FILE_CLOSEVIDEOFILE, OnFileCloseVideo)
	ON_COMMAND(ID_SETTINGS_SETPROPERTIES, OnSettingsSetProperties)
	ON_UPDATE_COMMAND_UI(ID_INDICATOR_GRID, OnUpdateGrid)
	ON_UPDATE_COMMAND_UI(ID_INDICATOR_NETWORK, OnUpdateNetworkStatus)
	ON_UPDATE_COMMAND_UI(ID_INDICATOR_FPS, OnUpdateFPSStatus)
	ON_UPDATE_COMMAND_UI(ID_INDICATOR_SAVE, OnUpdateSaveStatus)
	ON_UPDATE_COMMAND_UI(ID_INDICATOR_TRIGGER, OnUpdateTriggerStatus)
	ON_UPDATE_COMMAND_UI(ID_SETTINGS_SETMESURMENTUNITS, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(ID_SETTINGS_SETROI, onDisableWhenAnalyzing)
	ON_UPDATE_COMMAND_UI(ID_SETTINGS_SETVIRTUALPOLE, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(ID_RUN_STARTANALYZE, onDisableWhenAnalyzing)
	ON_UPDATE_COMMAND_UI(ID_RUN_STOPANALYZE, OnUpdateAnalyzeStop)
	ON_UPDATE_COMMAND_UI(IDR_FILE_SAVE_ANALYZED, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(IDR_SET_VP, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(IDR_SET_ROI, onDisableWhenAnalyzing)
	ON_UPDATE_COMMAND_UI(IDR_SET_MESURMENTS, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(IDR_FILE_SAVE_VIDEO, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(IDR_FILE_CLOSE_VIDEO, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(ID_FILE_CLOSEVIDEOFILE, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_ANALYZED_VIDEO, OnDataLoaded)
	ON_COMMAND(ID_FILE_STARTVIDEOSESSION, OnFileStartCaptureVideo)
	ON_UPDATE_COMMAND_UI(IDR_FILE_START_CAPTURE, OnUpdateFileStartVideoSession)
	ON_COMMAND(IDR_FILE_START_CAPTURE, OnFileStartCaptureVideo)
	ON_COMMAND(ID_FILE_CLOSEVIDEOSESSION, OnFileCloseVideo)
	ON_UPDATE_COMMAND_UI(ID_FILE_CLOSEVIDEOSESSION, OnDataLoaded)
	ON_UPDATE_COMMAND_UI(IDR_TRIGGER_ANALYZE, OnDataLoaded)
	ON_COMMAND(ID_RUN_STARTANALYZE, OnAnalyzeStart)
	ON_COMMAND(ID_RUN_STOPANALYZE, OnAnalyzeStop)
	ON_COMMAND(IDR_ANALYZE_SAVE, OnAnalyzeSave)
	ON_UPDATE_COMMAND_UI(IDR_ANALYZE_SAVE, OnUpdateAnalyzeSave)
	ON_COMMAND(ID_FILE_LISTENONEXTERNALPORT, OnListen)
	//}}AFX_MSG_MAP
	ON_COMMAND(ID_VIEW_TRIGGERINDICATOROVERLAYS, &CVirtualPoleView::OnViewTriggerIndicatorOverlays)
	ON_UPDATE_COMMAND_UI(ID_VIEW_TRIGGERINDICATOROVERLAYS, &CVirtualPoleView::OnUpdateViewTriggerIndicatorOverlays)
	ON_UPDATE_COMMAND_UI(ID_HELP_ONLINEHELP, &CVirtualPoleView::OnUpdateHelpOnlineHelp)
	ON_COMMAND(ID_HELP_ONLINEHELP, &CVirtualPoleView::OnHelpOnlineHelp)
	ON_COMMAND(ID_HELP_PROJECTONGOOGLECODE, &CVirtualPoleView::OnHelpProjectHomepage)
	ON_UPDATE_COMMAND_UI(ID_HELP_PROJECTONGOOGLECODE, &CVirtualPoleView::OnUpdateHelpProjectHomepage)
	ON_UPDATE_COMMAND_UI(ID_HELP_REPORTBUGS, &CVirtualPoleView::OnUpdateHelpReportBug)
	ON_COMMAND(ID_HELP_REPORTBUGS, &CVirtualPoleView::OnHelpReportBug)
	ON_COMMAND(ID_VIEW_RTFFT, &CVirtualPoleView::OnViewRTFFT)
	ON_UPDATE_COMMAND_UI(ID_VIEW_RTFFT, &CVirtualPoleView::OnUpdateViewRTFFT)
	ON_COMMAND(ID_VIEW_RTTRACKING, &CVirtualPoleView::OnViewRTTRACKING)
	ON_UPDATE_COMMAND_UI(ID_VIEW_RTTRACKING, &CVirtualPoleView::OnUpdateViewRTTRACKING)
	ON_COMMAND(ID_CONTROL_CAPTUREDARKFRAME, &CVirtualPoleView::OnControlCaptureDarkFrame)
	ON_UPDATE_COMMAND_UI(ID_CONTROL_CAPTUREDARKFRAME, &CVirtualPoleView::OnUpdateControlCaptureDarkFrame)
	ON_COMMAND(ID_VIEW_SETMOTIONMASK, &CVirtualPoleView::OnViewSetMotionMask)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CVirtualPoleView construction/destruction

CVirtualPoleView::CVirtualPoleView():m_bNoROIModal(false) {
	// set initial values
	m_hVideoThread					= NULL;
	m_bParamsSaved					= false;
	m_bData_Loaded					= false;
	m_bAnalyzing					= false;
	m_bListening					= false;
	m_bKeepMode						= false;
	m_bOriginSet					= false;
	m_bClosing						= false;
	m_imSize						= CSize(0,0);
	m_ptCurrent						= CPoint(0,0);
	m_ptPrevious					= CPoint(0,0);
	m_nMode							= 0;
	m_iError						= 0;
	m_iRawBufferNum					= 0;
	m_iAnalyzedBufferNum			= 0;
	m_iRawBufferSize				= 0;
	m_iAnalyzedBufferSize			= 0;
	m_StrechFactor.x				= 1.0;
	m_StrechFactor.y				= 1.0;
	m_Results.lFrameNumber			= 0;
	m_dSetPole.m_idx				= -1;

	// Default motion mask is the entire video frame
	CPoint cpDefMask[2];
	cpDefMask[0] = CPoint(0,0);
	cpDefMask[1] = CPoint(VP_ROI_MAX_WIDTH,VP_ROI_MAX_HEIGHT);
	m_crMotionMask.CreatePolygonRgn(cpDefMask, 2, ALTERNATE);

	// default settings
	m_Settings.boolSettings[0]		= false;
	m_Settings.boolSettings[1]		= false;
	m_Settings.boolSettings[3]		= false;
	m_Settings.boolSettings[2]		= false;
	m_Settings.boolSettings[4]		= true;
	m_Settings.boolSettings[5]		= true;
	m_Settings.boolSettings[6]		= true;
	m_Settings.boolSettings[7]		= true;
	m_Settings.boolSettings[8]		= true;
	m_Settings.boolSettings[9]		= true;
	m_Settings.boolSettings[10]		= false;
	m_Settings.boolSettings[11]		= false;
	m_Settings.boolSettings[12]		= true;
	m_Settings.boolSettings[13]		= false;	// !!NO LONGER USED!!
	m_Settings.boolSettings[14]		= true;		// trigger overlays
	m_Settings.boolSettings[15]		= false;	// real-time FFT
	m_Settings.boolSettings[16]		= false;	// enable frequency (FFT) trigger
	m_Settings.boolSettings[17]		= true;		// enable proximity/contact trigger
	m_Settings.boolSettings[18]		= true;		// real-time motion tracking
	m_Settings.boolSettings[19]		= false;	// detect motion only inside virtual objects
	m_Settings.boolSettings[20]		= false;	// enable motion detection

	m_Settings.intSettings[0]		= -1;		
	m_Settings.intSettings[1]		= 0;		
	m_Settings.intSettings[2]		= VP_ROI_MAX_WIDTH;	// Frame width 
	m_Settings.intSettings[3]		= VP_ROI_MAX_HEIGHT;// Frame height
	m_Settings.intSettings[4]		= 0;		
	m_Settings.intSettings[5]		= 0;		
	m_Settings.intSettings[6]		= 10;		// contrast threshold			

	m_Settings.intSettings[7]		= 10;		
	m_Settings.intSettings[8]		= 5;		
	m_Settings.intSettings[9]		= 800;		// framerate, frames/sec
	m_Settings.intSettings[10]		= 300;		// framebuffer, mbyte
	m_Settings.intSettings[11]		= 100000;	// max frames
	m_Settings.intSettings[12]		= 0;		// !!NO LONGER USED!!
	m_Settings.intSettings[13]		= 0;		// !!NO LONGER USED!!
	m_Settings.intSettings[14]		= 0;		// !!NO LONGER USED!!
	m_Settings.intSettings[15]		= 500;		// Exposure time (500 usec)
	m_Settings.intSettings[16]		= 2;		// Frequency SNR threshold
	m_Settings.intSettings[17]		= 5;		// Frequency bandpass LOW
	m_Settings.intSettings[18]		= 15;		// Frequency bandpass HIGH

	m_Settings.Paths[0]				= "C:\\Temp\\VPLogs\\";
	m_Settings.Paths[1]				= "C:\\Temp\\VPVideos\\";
	m_Settings.Paths[2]				= "C:\\Temp\\VPSettings\\";
	m_Settings.Paths[3]				= "ExSync, level-controlled";
	m_Settings.Paths[4]				= "RTSI_0";			// Camera control line
	m_Settings.Paths[5]				= "Immediate";		// Camera trigger IN line
	m_Settings.Paths[6]				= "External_0";		// Camera trigger OUT line
	m_Settings.Paths[7]				= "Dev3/ao0:1";		// Tracking output channels
	m_Settings.Paths[8]				= "";				// !! NO LONGER USED !!
	m_Settings.Paths[9]				= "None";			// Event trigger channel
	m_Settings.Paths[10]			= "Dev3/ctr0";		// Event trigger device
	m_Settings.Paths[11]			= "Dev3/ctr1";		// Indicator trigger input channel

	m_Settings.ConversionFactor.x	= 1.0;
	m_Settings.ConversionFactor.y	= 1.0;
	m_Settings.MedLineAngle			= 90.0;
	
	m_Settings.PoleList				= NULL;
	m_csRawVideo					= NULL;
	m_csAnalyzedVideo				= NULL;
	
	// assign some memory for the parameters 
	m_pAnalyze_Params = new Analyze_Params;

	for (int i = 0; i < (sizeof m_Results.MedianArray)/(sizeof m_Results.MedianArray[0]); i++) {
		m_PointsArray[i].x = 0;
		m_PointsArray[i].y = 0;
		m_AnglePointsArray[i].x = 0;
		m_AnglePointsArray[i].y = 0;
	}

	for (int i = 0; i < 256; i++) {
		m_FFTWArray[i].x = 0;
		m_FFTWArray[i].y = 0;
	}
	for (int i = 0; i < sizeof(m_Results.FFTWAmplitude)/sizeof(double); i++) {
		m_Results.FFTWAmplitude[i] = 0;
		m_Results.FFTWFrequency[i] = 0;
	}
}

CVirtualPoleView::~CVirtualPoleView() {
	m_bClosing = true;
	OnAnalyzeStop();

	m_memDcSourceFrame.SelectObject((CBitmap*)NULL);
	m_memDcSourceFrame.DeleteDC();
	m_bmSourceFrame.DeleteObject();

	m_memDcAnalyzedFrame.SelectObject((CBitmap*)NULL);
	m_memDcAnalyzedFrame.DeleteDC();
	m_bmAnalyzedFrame.DeleteObject();

	// clean up the parameters
	delete [] m_pAnalyze_Params;
	m_pAnalyze_Params = NULL;

	if (m_Settings.PoleList) {
		delete [] m_Settings.PoleList;
		m_Settings.PoleList = NULL;
	}
}

/////////////////////////////////////////////////////////////////////////////
// CVirtualPoleView drawing

void CVirtualPoleView::OnDraw(CDC* pDC) {
	CString s;
	CVirtualPoleDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	
	if (pDoc->isDataLoded()) {
		m_Results = pDoc->m_Results;
		if ((m_Results.bError) && (m_bAnalyzing)) {
			OnAnalyzeStop();
		}

		// Check if we have already analysed the current frame
		if ((m_LastAnalyzed == m_Results.lFrameNumber) && m_bAnalyzing) {
			return;
		} else {
			m_LastAnalyzed = m_Results.lFrameNumber;
		}

		// Copy the raw buffer to device context
		errChk(imgPlotDC ((GUIHNDL) m_memDcSourceFrame.m_hDC, 
			(void *)(pDoc->m_pRawBuffer+(m_iRawBufferNum * m_imSize.cx * m_imSize.cy)), 
			0, 0, m_imSize.cx, m_imSize.cy, 0, 0, IMGPLOT_MONO_8));

		// Check if buffer is empty (this might not be the best way to do it);
		//  Compare values of pixels (1,1) and (1,2). If both are == 205, this
		//  buffer is empty.
		// We also dont bother to update the analyzed image if buffer is empty.
		unsigned int nPixA = GetRValue(m_memDcSourceFrame.GetPixel(1,1));
		unsigned int nPixB = GetRValue(m_memDcSourceFrame.GetPixel(1,2));
		if ( !((nPixA == nPixB) && (nPixA == 205))){
			int idx = m_dSetPole.m_idx; // index of current object

			// Draw all objects
			if (idx > -1) {
				CPen penObj;
				penObj.CreatePen(PS_SOLID, 1, RGB(255, 255, 0));
				m_memDcSourceFrame.SelectObject(&penObj);
				for (int i=0; i < 10; i++) {
					CRect rcPoleRect = CRect(CPoint(m_dSetPole.m_lPolesX[i] - m_dSetPole.m_lPolesWidth[i] / 2,
						m_dSetPole.m_lPolesY[i] - m_dSetPole.m_lPolesHeight[i] / 2),
						CSize(m_dSetPole.m_lPolesWidth[i], m_dSetPole.m_lPolesHeight[i]));
					m_memDcSourceFrame.Rectangle(&rcPoleRect);
				}

			// Draw current active object (in different color)
				CPen penActObj;
				(pDoc->m_Results.bPoleTouch)
					? penActObj.CreatePen(PS_SOLID, 2, RGB(0, 255, 0))
					: penActObj.CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
				m_memDcSourceFrame.SelectObject(&penActObj);
				CRect rcPoleRect = CRect(CPoint(m_dSetPole.m_lPolesX[idx] - m_dSetPole.m_lPolesWidth[idx] / 2,
					m_dSetPole.m_lPolesY[idx] - m_dSetPole.m_lPolesHeight[idx] / 2),
					CSize(m_dSetPole.m_lPolesWidth[idx], m_dSetPole.m_lPolesHeight[idx]));
				m_memDcSourceFrame.Rectangle(&rcPoleRect);
			}

			// Draw motion mask
			if (m_iVertexCounter > 0) {
				CBrush GreenBrush;
				GreenBrush.CreateSolidBrush(RGB(150, 100, 100));
				m_memDcSourceFrame.FrameRgn(&m_crMotionMask, &GreenBrush, 1, 1);
			}

			// Get the rect for the image size
			CRect rcSource;
			m_rcVideo = ResizeVideoAspectRatio(m_imSize,true);
			rcSource.left	= 0;
			rcSource.top	= 0;
			rcSource.right	= m_Settings.intSettings[2];
			rcSource.bottom = m_Settings.intSettings[3];

			// Draw image
			pDC->StretchBlt(
				m_rcVideo.left,					// x-coordinate of upper-left corner of dest. rectangle
				m_rcVideo.top,					// y-coordinate of upper-left corner of dest. rectangle
				m_rcVideo.right-m_rcVideo.left, // width of destination rectangle
				m_rcVideo.bottom-m_rcVideo.top, // height of destination rectangle
				&m_memDcSourceFrame,			// the source device context
				rcSource.left,					// x-coordinate of upper-left corner of source rectangle
				rcSource.top,					// y-coordinate of upper-left corner of source rectangle
				rcSource.right,					// width of source rectangle
				rcSource.bottom,				// height of source rectangle
				SRCCOPY);						// raster operation code

			if (m_Settings.boolSettings[8] && m_bAnalyzing) {
				
				// Get analyzed image
				CSize AnalyzedSize = CSize(m_Settings.intSettings[2],m_Settings.intSettings[3]);
				m_rcVideo = ResizeVideoAspectRatio(AnalyzedSize,false);
				errChk(imgPlotDC ((GUIHNDL) m_memDcAnalyzedFrame.m_hDC, (void *)(pDoc->m_pAnalyzeBuffer+(m_iAnalyzedBufferNum * m_Settings.intSettings[2] * m_Settings.intSettings[3])), 0, 0,m_Settings.intSettings[2], m_Settings.intSettings[3], 0, 0, IMGPLOT_MONO_8));

				// Draw the time-varying trace of median whisker position
				CPen penPos(PS_SOLID, 1, RGB(200, 0, 0));
				pDC->SelectObject(&penPos);
				pDC->Polyline(m_PointsArray, 1024);

				// Draw a marker of current whisker position
				POINT CurrPos[2];
				double nX;
				nX = (double)pDoc->m_LastDAQOutput[0];
				nX = nX * (double(m_rcVideo.right-m_rcVideo.left) / (double)m_Settings.intSettings[2]);
				CurrPos[0].x = nX; //CurrPos[1].x = nX;
				CurrPos[0].y = 0; CurrPos[1].y = 12;
				CurrPos[1].x = nX + (CurrPos[1].y * pDoc->m_dSlope);
				pDC->Polyline(CurrPos, 2);

				// Draw the time-varying trace of whisker angle
				CPen penAngle(PS_SOLID, 1, RGB(0, 200, 0));
				pDC->SelectObject(&penAngle);
				pDC->Polyline(m_AnglePointsArray, 1024);

				// Draw power spectrum and power on preview frame (from FFTW)
				if (m_Settings.boolSettings[15]) {
					CPen penFFT(PS_SOLID, 1, RGB(100, 100, 100));
					pDC->SelectObject(&penFFT);
					pDC->Polyline(m_FFTWArray, 256); // POINT {x,y}

					// Re-draw bandpass region in different color
					POINT m_FFTWSubArray[256];
					int j = 0;
					for (int i = 1; i<256; i++){
							if (m_Results.FFTWFrequency[i]		>= m_Settings.intSettings[17]
								&& m_Results.FFTWFrequency[i]   <= m_Settings.intSettings[18]) {
								m_FFTWSubArray[j] = m_FFTWArray[i]; // POINT {x,y}
								j++;
							}
					}
					CPen penFFTh(PS_SOLID, 1, RGB(255, 255, 255));
					pDC->SelectObject(&penFFTh);
					pDC->Polyline(m_FFTWSubArray, j);

					// Show signal/noise in bandpass region (5-15 Hz?)
					m_csSignalNoise.Format("SN: %.2d", int(pDoc->m_Results.dFFTWBandPassSNR));
					m_memDcAnalyzedFrame.TextOut(1,1,m_csSignalNoise);
				}

				// Overlay frame number on analyzed video frame
				if (m_Settings.boolSettings[10]) {
					long frame_num = pDoc->m_Results.lFrameNumber;
					s.Format("%d",pDoc->m_Results.lFrameNumber);
					m_memDcAnalyzedFrame.TextOut(1,1,s);
				}

				pDC->StretchBlt(
					m_rcVideo.left,					// x-coordinate of upper-left corner of dest. rectangle
					m_rcVideo.top,					// y-coordinate of upper-left corner of dest. rectangle
					m_rcVideo.right-m_rcVideo.left, // width of destination rectangle
					m_rcVideo.bottom-m_rcVideo.top, // height of destination rectangle
					&m_memDcAnalyzedFrame,			// the source device context
					0,								// x-coordinate of upper-left corner of source rectangle
					0,								// y-coordinate of upper-left corner of source rectangle
					m_Settings.intSettings[2],		// width of source rectangle
					m_Settings.intSettings[3] ,		// height of source rectangle
					SRCCOPY );						// raster operation code
			}
		}
	}

	// Error handling
	Error:
	if(m_iError<0) {
		DisplayImaqError(m_iError);
		OnAnalyzeStop();
	}
}


/////////////////////////////////////////////////////////////////////////////
// CVirtualPoleView diagnostics

#ifdef _DEBUG
void CVirtualPoleView::AssertValid() const {
	CView::AssertValid();
}

void CVirtualPoleView::Dump(CDumpContext& dc) const {
	CView::Dump(dc);
}

CVirtualPoleDoc* CVirtualPoleView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CVirtualPoleDoc)));
	return (CVirtualPoleDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CVirtualPoleView message handlers


// lanches the ROI settings window and deals with the returned results
void CVirtualPoleView::OnSettingsSetROI() {
	if (m_nMode != 1) {
		//send data to the dialog
		m_dSetROI.m_lROI_Width	= m_Settings.intSettings[2];
		m_dSetROI.m_lROI_Height = m_Settings.intSettings[3];
		m_dSetROI.m_lROI_Left	= m_Settings.intSettings[4];
		m_dSetROI.m_lROI_Bottom = m_Settings.intSettings[5];
	}

	if (!m_bNoROIModal) {
		if (m_dSetROI.DoModal() == IDOK) {
			UpdateData(false);
		
			// Get new settings from closed dialog
			m_Settings.intSettings[2] = m_dSetROI.m_lROI_Width;
			m_Settings.intSettings[3] = m_dSetROI.m_lROI_Height;
			m_Settings.intSettings[4] = m_dSetROI.m_lROI_Left;
			m_Settings.intSettings[5] = m_dSetROI.m_lROI_Bottom;

		} else if(m_dSetROI.m_bROI_Mode) {
			m_nMode = 1;
			m_bKeepMode = true;
		}

	}
	m_bNoROIModal = false;

	CRect ROI = CRect(m_Settings.intSettings[4],m_Settings.intSettings[5],m_Settings.intSettings[2]+m_Settings.intSettings[4],m_Settings.intSettings[3]+m_Settings.intSettings[5]);

	// Get document class
	CVirtualPoleDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	RefreshAnalyzeParams();

	// Re-snap
	m_bData_Loaded = pDoc->OnFileStartCaptureVideo(&ROI, m_pAnalyze_Params);
	m_imSize = pDoc->GetFrameSize();
	
	// Update current frame bitmap 
	CDC *pDC;
	pDC = GetDC();
	m_memDcSourceFrame.SelectObject((CBitmap*)NULL);
	m_bmSourceFrame.DeleteObject();
	m_bmSourceFrame.CreateCompatibleBitmap(pDC,m_imSize.cx,m_imSize.cy);
	m_memDcSourceFrame.SelectObject(&m_bmSourceFrame);

	m_memDcAnalyzedFrame.SelectObject((CBitmap*)NULL);
	m_bmAnalyzedFrame.DeleteObject();
	m_bmAnalyzedFrame.CreateCompatibleBitmap(pDC,m_imSize.cx,m_imSize.cy);
	m_memDcAnalyzedFrame.SelectObject(&m_bmAnalyzedFrame);

	// Redraw frame
	ReleaseDC(pDC);
	InvalidateRect(NULL,true);
	UpdateWindow();

	// Remove current dark frame
	// Instead of clearing the buffer, just reset the indicator
	pDoc->m_bDarkFrameExists = false;
}

// Launch calibration dialog and handle returned settings
void CVirtualPoleView::OnSettingsSetMesurmentUnits() {
	if (m_nMode != 2) {
		//send data to the dialog
		m_dSetMesurement.m_bMesureMM			  = m_Settings.boolSettings[0];
		m_dSetMesurement.m_dpConversationFactor.x = m_Settings.ConversionFactor.x;
		m_dSetMesurement.m_dpConversationFactor.y = m_Settings.ConversionFactor.y;
		m_dSetMesurement.m_dMesurments_Angle	  = m_Settings.MedLineAngle;

		if (m_dSetMesurement.m_dMesurments_MM_X*m_Settings.ConversionFactor.x != m_dSetMesurement.m_dMesurments_Pixel_X) {
			m_dSetMesurement.m_dMesurments_MM_X = 1;
			m_dSetMesurement.m_dMesurments_Pixel_X = m_Settings.ConversionFactor.x;
		}

		if (m_dSetMesurement.m_dMesurments_MM_Y*m_Settings.ConversionFactor.y != m_dSetMesurement.m_dMesurments_Pixel_Y) {
			m_dSetMesurement.m_dMesurments_MM_Y = 1;
			m_dSetMesurement.m_dMesurments_Pixel_Y = m_Settings.ConversionFactor.y;
		}
	}

	if (m_dSetMesurement.DoModal() == IDOK) {
		UpdateData(false);
		
		// receive data back from dialog
		m_Settings.boolSettings[0]		= (bool)m_dSetMesurement.m_bMesureMM;
		m_Settings.ConversionFactor.x	= m_dSetMesurement.m_dpConversationFactor.x;
		m_Settings.ConversionFactor.y	= m_dSetMesurement.m_dpConversationFactor.y;
		m_Settings.MedLineAngle = m_dSetMesurement.m_dMesurments_Angle;
	} else if(m_dSetMesurement.m_iMesureMode>0) {
		m_nMode = 2;
		m_bKeepMode = true;
	}
}

// Launch the vitual object dialog and process returned results
void CVirtualPoleView::OnSettingsSetVirtualPole() {
	m_dSetPole.m_idx = m_Settings.intSettings[0];
	m_dSetPole.m_Count = m_Settings.intSettings[1];
	m_dSetPole.m_bNoPole = m_Settings.boolSettings[3];
	m_dSetPole.m_pStrings = m_Settings.PoleList;
	
	if (m_dSetPole.DoModal() == IDOK) {
		UpdateData(false);
		
		// recive data back from the dialog
		m_Settings.intSettings[0]	= m_dSetPole.m_idx;
		m_Settings.intSettings[1]	= m_dSetPole.m_Count;
		m_Settings.PoleList			= m_dSetPole.m_pStrings;
		m_Settings.boolSettings[3]  = (bool)(m_dSetPole.m_bNoPole);
		VPLocation = CRect(CPoint(m_dSetPole.m_lPole_XPos - m_dSetPole.m_lPole_Width / 2 ,
			m_dSetPole.m_lPole_YPos - m_dSetPole.m_lPole_Height / 2),
			CSize(m_dSetPole.m_lPole_Width,m_dSetPole.m_lPole_Height));

		// Store all virtual object coordinates
		for (int i = 0; i < 10; i++) {
			AllVPLocations[i] = CRect(CPoint(m_dSetPole.m_lPolesX[i] - m_dSetPole.m_lPolesWidth[i] / 2,
				m_dSetPole.m_lPolesY[i] - m_dSetPole.m_lPolesHeight[i] / 2),
				CSize(m_dSetPole.m_lPolesWidth[i], m_dSetPole.m_lPolesHeight[i]));
		}

		if (m_dSetPole.m_iVPMode == 1) {
			m_nMode = 3;
			m_bKeepMode = true;
		}
		if (m_dSetPole.m_iVPMode == 2) {
			InvalidateRect(NULL, false);
			UpdateWindow();
			m_nMode = 4;
			m_bKeepMode = true;
		}
	}
	InvalidateRect(NULL, false);
	UpdateWindow();
}

// lanches the Thresholds settings window and deals with the retruned results
void CVirtualPoleView::OnSettingsSetThresholds() {
	// send data to dialog
	m_dSetThreshold.m_iMotion_val				= m_Settings.intSettings[6];
	m_dSetThreshold.m_iPole_val					= m_Settings.intSettings[7];
	m_dSetThreshold.m_iMedianThreshold			= m_Settings.intSettings[8];
	m_dSetThreshold.m_lFreqSNRThreshold			= m_Settings.intSettings[16];
	m_dSetThreshold.m_lFrequencyBandpassLow		= m_Settings.intSettings[17];
	m_dSetThreshold.m_lFrequencyBandpassHigh	= m_Settings.intSettings[18];

	m_dSetThreshold.m_bEnableFrequencyTrigger = m_Settings.boolSettings[16];
	m_dSetThreshold.m_bEnableProximityTrigger = m_Settings.boolSettings[17];
	m_dSetThreshold.m_bLimitMotionToObjects   = (BOOL)(m_Settings.boolSettings[19]);
	m_dSetThreshold.m_bDetectMotion			  = (BOOL)(m_Settings.boolSettings[20]);

	m_dSetThreshold.m_csTrackingOutputCh1 = m_Settings.Paths[7];
	m_dSetThreshold.m_csEventTriggerCh	  = m_Settings.Paths[9];
	m_dSetThreshold.m_csEventTriggerDev	  = m_Settings.Paths[10];
	m_dSetThreshold.m_csIndicatorTriggerINCh = m_Settings.Paths[11];

	if (m_dSetThreshold.DoModal() == IDOK) {
		UpdateData(false);
		
		// get data back from the dialog
		m_Settings.intSettings[6]   = m_dSetThreshold.m_iMotion_val;
		m_Settings.intSettings[8]   = m_dSetThreshold.m_iMedianThreshold;
		m_Settings.intSettings[16]  = m_dSetThreshold.m_lFreqSNRThreshold;
		m_Settings.intSettings[17]  = m_dSetThreshold.m_lFrequencyBandpassLow;
		m_Settings.intSettings[18]  = m_dSetThreshold.m_lFrequencyBandpassHigh;

		m_Settings.boolSettings[16] = (bool)(m_dSetThreshold.m_bEnableFrequencyTrigger); 
		m_Settings.boolSettings[17] = (bool)(m_dSetThreshold.m_bEnableProximityTrigger); 
		m_Settings.boolSettings[19] = (bool)(m_dSetThreshold.m_bLimitMotionToObjects);
		m_Settings.boolSettings[20] = (bool)(m_dSetThreshold.m_bDetectMotion);

		m_Settings.Paths[7]  = m_dSetThreshold.m_csTrackingOutputCh1;
		m_Settings.Paths[9]  = m_dSetThreshold.m_csEventTriggerCh;
		m_Settings.Paths[10] = m_dSetThreshold.m_csEventTriggerDev;
		m_Settings.Paths[11] = m_dSetThreshold.m_csIndicatorTriggerINCh;

		if (m_dSetThreshold.m_bApplay)
			OnSettingsSetThresholds();
	}
}

// lanches the Properties window and deals with the retruned results
void CVirtualPoleView::OnSettingsSetProperties() {
	//send data to the dialog
	m_dSetProperties.m_bSaveLog				= m_Settings.boolSettings[2];;
	m_dSetProperties.m_iFPS					= m_Settings.intSettings[9];
	m_dSetProperties.m_iBufSize				= m_Settings.intSettings[10];
	m_dSetProperties.m_lMaxVideoLength		= m_Settings.intSettings[11];
	m_dSetProperties.m_lFrameExposure		= m_Settings.intSettings[15];
	m_dSetProperties.m_csLogFilePath		= m_Settings.Paths[0];
	m_dSetProperties.m_csVideoFilePath		= m_Settings.Paths[1];
	m_dSetProperties.m_csSettingsPath		= m_Settings.Paths[2];
	m_dSetProperties.m_csCameraMode			= m_Settings.Paths[3];
	m_dSetProperties.m_csCameraControlLine	= m_Settings.Paths[4];
	m_dSetProperties.m_csCameraTriggerLine	= m_Settings.Paths[5];
	m_dSetProperties.m_csCameraTriggerOutLine	= m_Settings.Paths[6];
	
	// send the settings structure for saving
	m_dSetProperties.m_SaveData = m_Settings;
	
	// Launch the dialog
	if (m_dSetProperties.DoModal() == IDOK)	{
		UpdateData(false);
		
		// receive data from the dialog
		m_Settings = m_dSetProperties.m_SaveData;
		InvalidateRect(NULL,false);
		UpdateWindow();
	}	
}

// Shows or hides the Virtual Pole markings
void CVirtualPoleView::OnViewVirtualPole() {
	m_Settings.boolSettings[7] = !m_Settings.boolSettings[7];
	InvalidateRect(NULL,false);
	UpdateWindow();
}

// Helper function to the OnViewVirtualPole
void CVirtualPoleView::OnUpdateViewVirtualPole(CCmdUI* pCmdUI) {
	pCmdUI->SetCheck(m_Settings.boolSettings[7]);
	pCmdUI->Enable(m_bData_Loaded);
}

// keeps or neglects the aspect ration of the showed area
void CVirtualPoleView::OnViewKeepaspectratio() {
	m_Settings.boolSettings[12] = !m_Settings.boolSettings[12];
	InvalidateRect(NULL,true);
	UpdateWindow(); 
}

// helper function to the OnViewKeepaspectratio
void CVirtualPoleView::OnUpdateViewKeepaspectratio(CCmdUI* pCmdUI) {
	pCmdUI->SetCheck(m_Settings.boolSettings[12]);
	pCmdUI->Enable(m_bData_Loaded);
}

// Enables controls if theres an active session 
void CVirtualPoleView::OnDataLoaded(CCmdUI *pCmdUI) {
		pCmdUI->Enable(m_bData_Loaded);
}

// closes the current session
void CVirtualPoleView::OnFileCloseVideo() {
	// Update ROI parameters in case these were updated from a file
	if (m_dSetROI.m_lROI_Width < 0 || m_dSetROI.m_lROI_Width > VP_ROI_MAX_WIDTH) {
		m_Settings.intSettings[2] = VP_ROI_MAX_WIDTH;
		m_Settings.intSettings[3] = VP_ROI_MAX_HEIGHT;
		m_Settings.intSettings[4] = 0;
		m_Settings.intSettings[5] = 0;	
	} else {
		m_Settings.intSettings[2] = m_dSetROI.m_lROI_Width;
		m_Settings.intSettings[3] = m_dSetROI.m_lROI_Height;
		m_Settings.intSettings[4] = m_dSetROI.m_lROI_Left;
		m_Settings.intSettings[5] = m_dSetROI.m_lROI_Bottom;
	}

	// Save backup of settings
	m_Settings_backup = m_Settings;
	m_bParamsSaved = true;

	if (m_bAnalyzing)
		OnAnalyzeStop();

	CVirtualPoleDoc* pDoc = GetDocument();

	m_memDcSourceFrame.SelectObject((CBitmap*)NULL);
	m_bmSourceFrame.DeleteObject();

	m_memDcAnalyzedFrame.SelectObject((CBitmap*)NULL);
	m_bmAnalyzedFrame.DeleteObject();

	pDoc->CloseVideo();
	m_bData_Loaded = false;

	InvalidateRect(NULL, true);
	UpdateWindow();
}

/* OnLButtonDown
	Handle responses to mouse-down clicks in main frame.
	Initiates calibration and ROI commands.

	See also: OnSettingsSetROI()	Initiates ROI dialog
*/
void CVirtualPoleView::OnLButtonDown(UINT nFlags, CPoint point) {
	// Check flags
	switch (nFlags) {
		case 3:	// right+left mouse key	= Abort/Done
			InvalidateRect(m_rcVideo, false);
			UpdateWindow();
			m_nMode = 0;
			break;
		case 5:	// shift key held down	= Undo last click
			switch (m_nMode) {
				case 5: // polygon drawing mode
					m_iVertexCounter--;
					break;
			}
			break;
	}

	if (m_nMode > 0) {
		// Get the current point
		m_ptCurrent = point;

		// If there's no origin selected then set the origin
		if (!m_bOriginSet) {
			m_ptOrigin = point;
			
			// Initialize vertex counter for polygon drawing
			if (m_nMode == 5) {
				m_iVertexCounter = 0;
				m_ptMaskVertices[0] = m_ptOrigin;
				m_bOriginSet = true;
				m_ptPrevious = m_bOriginSet;
			}

		} else {
			// Delete old graphics 
			InvalidateRect(NULL, true);
			UpdateWindow();
		}
		
		switch (m_nMode) {
		case 1:
			// ROI mode
			// if there's no origin selected then set the rects
			if (!m_bOriginSet) {
				m_rcCurr.OffsetRect(m_ptCurrent);
				m_rcPrev.OffsetRect(m_ptOrigin);
			} else {
				// return selected area to the ROI selection dialog
				dPoint dpOrg, dpCurr;
				CRect rcROI;

				dpOrg =  Screen2Frame(m_ptOrigin);
				dpCurr = Screen2Frame(m_ptCurrent);
				
				rcROI.left	= (int) floor(dpOrg.x);
				rcROI.top	= (int) floor(dpOrg.y);
				rcROI.right	= (int) floor(dpCurr.x);
				rcROI.bottom= (int) floor(dpCurr.y);
				
				rcROI.NormalizeRect();

				// Store values that will go to dialog
				m_dSetROI.m_lROI_Bottom	= rcROI.top;
				m_dSetROI.m_lROI_Height	= rcROI.bottom - rcROI.top;
				m_dSetROI.m_lROI_Left	= rcROI.left;
				m_dSetROI.m_lROI_Width	= rcROI.right - rcROI.left;

				// Get current ROI
				unsigned long nWidth, nHeight, nLeft, nBottom;
				nWidth = m_Settings.intSettings[2];
				nHeight = m_Settings.intSettings[3];
				nLeft = m_Settings.intSettings[4];
				nBottom = m_Settings.intSettings[5];

				// Adjust values according to current ROI
				m_dSetROI.m_lROI_Bottom	= m_dSetROI.m_lROI_Bottom + nBottom;
				m_dSetROI.m_lROI_Left = m_dSetROI.m_lROI_Left + nLeft;

				m_Settings.intSettings[2] = m_dSetROI.m_lROI_Width;
				m_Settings.intSettings[3] = m_dSetROI.m_lROI_Height;
				m_Settings.intSettings[4] = m_dSetROI.m_lROI_Left;
				m_Settings.intSettings[5] = m_dSetROI.m_lROI_Bottom;

				// Reset the rectangles (??)
				m_rcCurr	= CRect (CPoint(0,0), CSize (1,1));
				m_rcPrev	= m_rcCurr;

				// Reset motion mask to default (entire frame)
				CPoint cpDefMask[2];
				cpDefMask[0] = CPoint(0,0);
				cpDefMask[1] = CPoint(VP_ROI_MAX_WIDTH,VP_ROI_MAX_HEIGHT);
				CRgn crTempReg;
				crTempReg.CreatePolygonRgn(cpDefMask, 2, ALTERNATE);
				m_crMotionMask.CopyRgn(&crTempReg);

				// Reset MotionMask bitmap (used for real-time masking operations)
				CVirtualPoleDoc* pDoc = GetDocument(); // document class
				ASSERT_VALID(pDoc);
				pDoc->m_pMotionMaskBuffer = new byte[m_imSize.cx*m_imSize.cy]; // initialize

				// All pixels are by default inside region after a ROI reset
				int indx;
				for (int row=0; row < m_imSize.cy; row++) { // iterate over all pixels
					for (int col=0; col < m_imSize.cx; col++) {
						indx = ((max(0, row-1))*m_imSize.cx) + col;
						pDoc->m_pMotionMaskBuffer[indx] = 255;
					}
				}

				// Update frame (m_bNoROIModal prevents ROI dialog from showing)
				m_bNoROIModal = true;
				OnSettingsSetROI();
				
				InvalidateRect(NULL, false);
				UpdateWindow();
			}
			break;

		case 2:
			// Calibration mode
			if (m_bOriginSet) {
				// Return selected area to the Measurements dialog
				dPoint dpOrg, dpCurr;
				
				dpOrg = Screen2Frame(m_ptOrigin);
				dpCurr = Screen2Frame(m_ptCurrent);

				m_dSetMesurement.m_dMesurments_RefLinePoints[0] = m_ptOrigin;
				m_dSetMesurement.m_dMesurments_RefLinePoints[1] = m_ptCurrent;

				double Num_Pixels = sqrt(pow(dpCurr.x - dpOrg.x,2)+pow(dpCurr.y - dpOrg.y,2));

				if (m_dSetMesurement.m_iMesureMode == 1) {
					m_dSetMesurement.m_dMesurments_Pixel_X = ceil(Num_Pixels*100)/100;
					m_dSetMesurement.m_dpConversationFactor.x = 1.0 / Num_Pixels;
				}
				
				else if (m_dSetMesurement.m_iMesureMode == 2) {
					m_dSetMesurement.m_dMesurments_Pixel_Y = ceil(Num_Pixels*100)/100;
					m_dSetMesurement.m_dpConversationFactor.y = 1.0 / Num_Pixels;
				}

				else if (m_dSetMesurement.m_iMesureMode == 3) {
					double Angle = atan((dpCurr.x - dpOrg.x)/(dpCurr.y - dpOrg.y));
					m_dSetMesurement.m_dMesurments_Angle = ceil(100 * Angle / PI) * 1.8;
				}

				// Show the dialog again for confirmation
				OnSettingsSetMesurmentUnits();
			} else {
				// if there's no origin selected then set the rect
				m_rcPrev = CRect (point, CSize (1,1));
			}
			break;
		case 3:
			// Virtual pole set mode
			if (m_bOriginSet) {
				// Return selected VP and VP area to the VP selection dialog
				m_ptOrigin.x  = (int) floor((double)m_ptOrigin.x  / m_StrechFactor.x);
				m_ptOrigin.y  = (int) floor((double)m_ptOrigin.y  / m_StrechFactor.y);
				m_ptCurrent.x = (int) floor((double)m_ptCurrent.x / m_StrechFactor.x);
				m_ptCurrent.y = (int) floor((double)m_ptCurrent.y / m_StrechFactor.y);
				
				m_dSetPole.m_lPole_XPos		= m_ptOrigin.x;
				m_dSetPole.m_lPole_YPos		= m_ptOrigin.y;
				m_dSetPole.m_lPole_Width	= 2 * abs(m_ptOrigin.x-m_ptCurrent.x);
				m_dSetPole.m_lPole_Height	= 2 * abs(m_ptOrigin.y-m_ptCurrent.y);

				// Reset the rectangles
				m_rcCurr	= CRect (CPoint(0,0), CSize (1,1));
				m_rcPrev	= m_rcCurr;

				// Return to dialog for confirmation
				OnSettingsSetVirtualPole();

				// draw the VP
				InvalidateRect(NULL, false);
				UpdateWindow();
			} else {
				// if there's no origin selected then set the rect
				m_rcCurr= CRect (point, CSize (1,1));
			}
			break;
		case 4: // return to Pole window mode
			OnSettingsSetVirtualPole();
			break;
		case 5:
			// Motion mask (polygon) drawing mode
			
			// Create polygon region (of the CRgn class; used for display)
			dPoint dpVertex;
			dpVertex = Screen2Frame(m_ptCurrent);
			m_iVertexCounter++;
			m_ptMaskVertices[m_iVertexCounter].x = (int) floor(dpVertex.x);
			m_ptMaskVertices[m_iVertexCounter].y = (int) floor(dpVertex.y);
			m_ptPrevious = m_ptCurrent;
			CRgn crTempReg;
			crTempReg.CreatePolygonRgn(m_ptMaskVertices, m_iVertexCounter+1, ALTERNATE);
			m_crMotionMask.CopyRgn(&crTempReg);

			// Create MotionMask bitmap (used for real-time masking operations)
			CVirtualPoleDoc* pDoc = GetDocument(); // document class
			ASSERT_VALID(pDoc);
			pDoc->m_pMotionMaskBuffer = new byte[m_imSize.cx*m_imSize.cy]; // initialize

			// Find pixels that are inside region
			int indx;
			for (int row=0; row < m_imSize.cy; row++) { // iterate over all pixels
				for (int col=0; col < m_imSize.cx; col++) {
					indx = ((max(0, row-1))*m_imSize.cx) + col;
					if ( m_crMotionMask.PtInRegion(col, row) ) {
						pDoc->m_pMotionMaskBuffer[indx] = 255;
					} else {
						pDoc->m_pMotionMaskBuffer[indx] = 0;
					}
				}
			}
			break;
		}
		
		if (m_bOriginSet && !m_bKeepMode && m_nMode != 5)
			m_nMode = 0; // stops all modes

		if (m_nMode != 5) {
			m_bOriginSet = !m_bOriginSet; // toggles m_bOriginSet
			m_bKeepMode = false;
		}
	} else {
		m_bOriginSet = false;
		m_bKeepMode = false;
	}
	CView::OnLButtonDown(nFlags, point);
}

// Initialize some of the graphics
int CVirtualPoleView::OnCreate(LPCREATESTRUCT lpCreateStruct) {
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// get the system's DC
	CDC *pDC;
	pDC = GetDC();
 
	// Create all the memory DC's
	m_memDcSourceFrame.CreateCompatibleDC(pDC);
	m_memDcAnalyzedFrame.CreateCompatibleDC(pDC);
  
	//release the system DC
	ReleaseDC (pDC);
	
	// initialize shape objects
	m_rcCurr	= CRect (CPoint(0,0), CSize (1,1));
	m_rcPrev	= m_rcCurr;
	m_RectSize	= CSize (2,2);

	// Set default for the dc's
	m_memDcSourceFrame.SelectStockObject(NULL_BRUSH);
	m_memDcSourceFrame.SelectStockObject(WHITE_PEN);
	m_memDcAnalyzedFrame.SelectStockObject(NULL_BRUSH);
	m_memDcAnalyzedFrame.SelectStockObject(WHITE_PEN);
	return 0;
}

// Deals with mouse movements
void CVirtualPoleView::OnMouseMove(UINT nFlags, CPoint point) {

	if (m_bData_Loaded)
		// get the current point
		m_ptCurrent = point;	
	if ((m_bOriginSet) && (m_nMode > 0)) {
		// get the client DC
		CClientDC dc(this);
		
		CBrush GreenBrush;
		GreenBrush.CreateSolidBrush(RGB(0,255,0));
		CPen GreenPen(PS_SOLID, 1, RGB(0, 255, 0));
		CPen WhitePen(PS_SOLID, 1, RGB(255, 255, 255));

		switch (m_nMode) {
		case 1:
			m_rcCurr = CRect(m_ptOrigin,m_ptCurrent);
			m_rcCurr.NormalizeRect();

			dc.DrawDragRect (&m_rcCurr, m_RectSize, &m_rcPrev, m_RectSize, &GreenBrush, NULL);
			m_rcPrev = m_rcCurr;
			break;

		case 2:
			// Delete the old line
			InvalidateRect(m_rcPrev,false);
			UpdateWindow();
			
			// Draw the line
			dc.SelectObject(&GreenPen);
			dc.MoveTo(m_ptOrigin);
			dc.LineTo(m_ptCurrent);

			// Get the rect for next time
			m_rcPrev = CRect(m_ptOrigin,m_ptCurrent);
			m_rcPrev.NormalizeRect();
			m_rcPrev.InflateRect(1,1); // to be on the safe side
			break;
		
		case 3:
			m_rcCurr = CRect(CPoint(2*m_ptOrigin.x-point.x,2*m_ptOrigin.y-point.y),CSize(2*(point.x-m_ptOrigin.x),2*(point.y-m_ptOrigin.y)));
			m_rcCurr.NormalizeRect();
			dc.DrawDragRect (&m_rcCurr, m_RectSize, &m_rcPrev, m_RectSize, NULL,NULL);
			m_rcPrev = m_rcCurr;
			break;
		case 4:
			break;
		case 5:
			// Motion mask (polygon) drawing mode

			// Blank frame (removes old lines)
			InvalidateRect(m_rcVideo, false);
			UpdateWindow();

			// Draw line between current points and last point stored in masking polygon
			dc.SelectObject(&WhitePen);
			dc.MoveTo(m_ptPrevious);
			dc.LineTo(m_ptCurrent);

			// Draw current polygon
			//dc.SelectObject(&GreenPen);
			//dc.Polyline(m_ptMaskVertices, m_iVertexCounter+1);

			break;
		}
	}
	CView::OnMouseMove(nFlags, point);
}

// Sets the mouse mode to set ROI on frame
void CVirtualPoleView::OnToolBarSetRoi() {
	m_nMode = 1;
}

// sets the mouse mode to measure distance on frame
void CVirtualPoleView::OnToolBarSetMesurmentUnits() {
	m_nMode = 2;
	m_dSetMesurement.m_iMesureMode=3;
}

// sets the mouse mode to set the VP on frame
void CVirtualPoleView::OnToolBarSetVP() {
	m_nMode = 3;
	m_dSetPole.m_iVPMode=1;
}

// calculates the video size 
CRect CVirtualPoleView::ResizeVideoAspectRatio(CSize imSize, bool bSourceVideo){
	CRect VideoSize;
	GetClientRect(&VideoSize);
	int shift = VideoSize.right/2;
	VideoSize.right = shift;

 	if (!m_Settings.boolSettings[12]) {
		m_StrechFactor.x = (double)abs(VideoSize.right - VideoSize.left) / m_imSize.cx;
		m_StrechFactor.y = (double)abs(VideoSize.bottom - VideoSize.top) / m_imSize.cy;
		if (!bSourceVideo) 
			VideoSize.OffsetRect(shift,0);
		return VideoSize;
	}
	
	double AspectRatio = (double)imSize.cy / imSize.cx;

	if (AspectRatio <= 1)
		VideoSize = CRect(VideoSize.left,VideoSize.top,VideoSize.right,(int)floor(VideoSize.right*AspectRatio));
	else
		VideoSize = CRect(VideoSize.left,VideoSize.top,(int)floor((double)VideoSize.bottom / AspectRatio),VideoSize.bottom);

	m_StrechFactor.x = (double)abs(VideoSize.right - VideoSize.left) / m_imSize.cx;
	m_StrechFactor.y = (double)abs(VideoSize.bottom - VideoSize.top) / m_imSize.cy;
	if (!bSourceVideo) 
		VideoSize.OffsetRect(shift,0);
	return VideoSize;
}

// Converts mouse location to coordinates on the real frame
dPoint CVirtualPoleView::Screen2Frame(CPoint CurrLoc) {
	dPoint dpRealLoc;
	
	// Find location in true pixels by divding the the coordinate by the stretch factor	
	dpRealLoc.x = (double)CurrLoc.x / m_StrechFactor.x;
	dpRealLoc.y = (double)CurrLoc.y / m_StrechFactor.y;
	
	// If we use millimeters then convert result to mm
	if (m_Settings.boolSettings[0]) {	
		dpRealLoc.x *= m_Settings.ConversionFactor.x;
		dpRealLoc.y *= m_Settings.ConversionFactor.y;
	}
	return dpRealLoc;	
}

// exits the program
void CVirtualPoleView::OnFileExit() {
	PostQuitMessage(0);
}

/*  Start video preview
	This function is started by user-action in the GUI or by TCP commands
*/
void CVirtualPoleView::OnAnalyzeStart() {
	int NoPole = 0;
	if (m_Settings.boolSettings[3] == false) {
		// Checks is a virtual object has been defined
		if (m_Settings.intSettings[0] < 0 || m_Settings.intSettings[0] >= m_Settings.intSettings[1]) {
			NoPole = IDYES;
			if (NoPole == IDNO)
				return;
		}
	}

	// Start analysis mode and enable STOP button
	m_bAnalyzing = true;

	// Get pointer to document
	CVirtualPoleDoc* pDoc = GetDocument();
	
	// Find the number of frames in the raw and analyzed buffers 
	m_iRawBufferSize	  = (ULONG)floor((double)(m_Settings.intSettings[10]*1048576) / (double)(m_imSize.cx * m_imSize.cy));
	m_iAnalyzedBufferSize = __max(5,(unsigned int)ceil((double)m_Settings.intSettings[9] / (double)60)); // max(5, FPS/60)

	// Allocate memory for the critical section objects
	m_csRawVideo		= new CRITICAL_SECTION[m_iRawBufferSize]; // size in MByte is configured in GUI
	m_csAnalyzedVideo	= new CRITICAL_SECTION[m_iAnalyzedBufferSize];
	
	// Initialize the critical section objects
	for (unsigned int i = 0; i < m_iRawBufferSize; i++)
		InitializeCriticalSection((m_csRawVideo+i));

	for (unsigned int i = 0; i < m_iAnalyzedBufferSize; i++)	
		InitializeCriticalSection((m_csAnalyzedVideo+i));

	// If a virtual object has been defined, then update its parameters
	if (NoPole == 0 && m_Settings.boolSettings[3] == false) {
		CString CurrentString		= *(m_Settings.PoleList+m_Settings.intSettings[0]);
		m_dSetPole.m_lPole_XPos		= atol(CurrentString.Mid(5,3));
		m_dSetPole.m_lPole_YPos		= atol(CurrentString.Mid(22,3));
		m_dSetPole.m_lPole_Width	= atol(CurrentString.Mid(36,3));
		m_dSetPole.m_lPole_Height	= atol(CurrentString.Mid(47,3));
	}
	
	// Object parameters if no virtual object has been defined
	else {
		m_dSetPole.m_lPole_XPos		= -5;
		m_dSetPole.m_lPole_YPos		= -5;
		m_dSetPole.m_lPole_Width	= 2;
		m_dSetPole.m_lPole_Height	= 2;
		m_Settings.boolSettings[3]	= false;
		VPLocation					= CRect(0,0,2,2);
	}

	// Saved settings for later (when we reset acqisition)
	m_Settings_backup = m_Settings;
	m_bParamsSaved = true;

	// Refresh tresholds, VP location, buffers sizes etc.
	RefreshAnalyzeParams();

	// Conversion factor
	if (m_Settings.boolSettings[0])	{
		(*m_pAnalyze_Params).ConversionFactor.x = m_Settings.ConversionFactor.x;
		(*m_pAnalyze_Params).ConversionFactor.y = m_Settings.ConversionFactor.y;
	} else {
		(*m_pAnalyze_Params).ConversionFactor.x = 1;
		(*m_pAnalyze_Params).ConversionFactor.y = 1;
	}
	
	// Video size limit
	if (m_dSetProperties.m_bVideoLength)
		(*m_pAnalyze_Params).FrameLimit = m_Settings.intSettings[11];
	else
		(*m_pAnalyze_Params).FrameLimit = -1;
	
	// Start analysis thread
	bool analyzerOK = pDoc->OnStartAnalyze(m_pAnalyze_Params);
	
	// Abort if an error occurred
	if (!analyzerOK) {
		pDoc->TriggerStop();
		m_bAnalyzing = false;
		return;
	}
	
	// Start the show video thread
	m_hVideoThread = AfxBeginThread(VideoThread, this, THREAD_PRIORITY_NORMAL, 0, 0, NULL);   
	
	// if failed to create the video thread - get out
	if (!m_hVideoThread) {
		pDoc->TriggerStop();
		m_bAnalyzing = false;
		return;
	}
	m_hVideoThread -> m_bAutoDelete = false;
}

// Shows or hide source video
void CVirtualPoleView::OnViewSourceVideo() {
	m_Settings.boolSettings[9] = !m_Settings.boolSettings[9];
}

// Helper function to OnViewSourceVideo 
void CVirtualPoleView::OnUpdateViewSourceVideo(CCmdUI* pCmdUI) {
	pCmdUI->SetCheck(m_Settings.boolSettings[9]);
	pCmdUI->Enable(m_bData_Loaded);
}

// Show or hide the analyzed video frame (right part of window)
void CVirtualPoleView::OnViewAnalyzedVideo() {	
	m_Settings.boolSettings[8] = !m_Settings.boolSettings[8]; 
}

// Helper function to OnViewSourceVideo 
void CVirtualPoleView::OnUpdateViewAnalyzedVideo(CCmdUI* pCmdUI) {
	pCmdUI->SetCheck(m_Settings.boolSettings[8]);
	pCmdUI->Enable(m_bData_Loaded);
}

// Overlay the framenumber on grabbed video frame (analysed frame)
void CVirtualPoleView::OnUpdateFrameNum(CCmdUI* pCmdUI) {
	CString strFrameNum;
	strFrameNum.Format ("Frame: %1.0f", m_Results.lFrameNumber);
	pCmdUI->Enable(true);
	pCmdUI->SetText(strFrameNum);
}

// Show/hide frame numbers on screen
void CVirtualPoleView::OnViewFrameNumbers() {
	m_Settings.boolSettings[10] = !m_Settings.boolSettings[10];
	InvalidateRect(NULL,false);
	UpdateWindow();
}

// Helper function to the OnViewFrameNumbers
void CVirtualPoleView::OnUpdateViewFrameNumbers(CCmdUI* pCmdUI) {
	pCmdUI->SetCheck(m_Settings.boolSettings[10]);
	pCmdUI->Enable(m_bData_Loaded);
}

// Show/hide trigger indicator overlays in video frame
void CVirtualPoleView::OnViewTriggerIndicatorOverlays() {
	m_Settings.boolSettings[14] = !m_Settings.boolSettings[14];
	InvalidateRect(NULL,false);
	UpdateWindow();
}

// Updated CHECKED status on menu item 'Trigger Indicator Overlays'
void CVirtualPoleView::OnUpdateViewTriggerIndicatorOverlays(CCmdUI *pCmdUI) {
	pCmdUI->SetCheck(m_Settings.boolSettings[14]);
	pCmdUI->Enable(m_bData_Loaded);
}

// Update pixel coordinates in status bar (where the mouse pointers hovers above the video frame)
void CVirtualPoleView::OnUpdateGrid(CCmdUI *pCmdUI) {
	if (m_bData_Loaded)	{
		CString strGrid;
		dPoint dpLoc;
		
		dpLoc = Screen2Frame(m_ptCurrent);
		if (m_Settings.boolSettings[0])
			strGrid.Format ("X:%1.3f, Y:%1.3f mm", dpLoc.x,dpLoc.y);
		else
			strGrid.Format ("X:%1.3f, Y:%1.3f pix", dpLoc.x,dpLoc.y);
		
		pCmdUI->Enable(true);
		pCmdUI->SetText(strGrid);
	} else {
		pCmdUI->SetText("");
		pCmdUI->Enable(false);
	}
}

// Intialize a new session and call the doc calls to snap a frame
void CVirtualPoleView::OnFileStartCaptureVideo() {
	CVirtualPoleDoc* pDoc = GetDocument();

	// Refresh GUI defined parameters
	RefreshAnalyzeParams();

	if (m_bParamsSaved) {
		// Use previous settings
		m_Settings = m_Settings_backup;
		CRect ROI = CRect(m_Settings.intSettings[4],m_Settings.intSettings[5],m_Settings.intSettings[2]+m_Settings.intSettings[4],m_Settings.intSettings[3]+m_Settings.intSettings[5]);
		m_bData_Loaded = pDoc->OnFileStartCaptureVideo(&ROI, m_pAnalyze_Params);
	} else {
		// call the Doc class to open the image and load it
		m_bData_Loaded = pDoc->OnFileStartCaptureVideo(0, m_pAnalyze_Params);
	}

	if (m_bData_Loaded) {			
		m_Results.lFrameNumber = 0;
		// Get the image size and update the ROI dialog
		m_imSize = pDoc->GetFrameSize();
		
		if (!m_bParamsSaved) {
			// Set ROI to full frame
			m_Settings.intSettings[2] = m_imSize.cx;
			m_Settings.intSettings[3] = m_imSize.cy;
			m_Settings.intSettings[4] = 0;
			m_Settings.intSettings[5] = 0;
		}

		// Updating current frame bitmap 
		CDC *pDC;
		pDC = GetDC();
		m_memDcSourceFrame.SelectObject((CBitmap*)NULL);
		m_bmSourceFrame.DeleteObject();
		m_bmSourceFrame.CreateCompatibleBitmap(pDC,m_imSize.cx,m_imSize.cy);
		m_memDcSourceFrame.SelectObject(&m_bmSourceFrame);

		m_memDcAnalyzedFrame.SelectObject((CBitmap*)NULL);
		m_bmAnalyzedFrame.DeleteObject();
		m_bmAnalyzedFrame.CreateCompatibleBitmap(pDC,m_imSize.cx,m_imSize.cy);
		m_memDcAnalyzedFrame.SelectObject(&m_bmAnalyzedFrame);

		ReleaseDC(pDC);

		// Draw frame
		InvalidateRect(NULL, true);
		UpdateWindow();
	}	
}

// Enable STOP button if analysis is running
void CVirtualPoleView::OnUpdateAnlyzeStart(CCmdUI* pCmdUI) {
	pCmdUI->Enable(m_bData_Loaded && !m_bAnalyzing);
}

// Disable STOP button if analysis is not running
void CVirtualPoleView::OnUpdateAnalyzeStop(CCmdUI* pCmdUI) {
	pCmdUI->Enable(m_bAnalyzing);
}

// Stop image acquisition and real-time analysis
void CVirtualPoleView::OnAnalyzeStop() {
	if (m_bAnalyzing) {
		// Get document
		CVirtualPoleDoc* pDoc = GetDocument();

		m_bAnalyzing = false;
		pDoc->TriggerStop();

		DWORD dwResult;

		// Stop video thread with a 5000 msec timeout
		dwResult = WaitForSingleObject(m_hVideoThread->m_hThread, 5000);

		// If the wait timed-out, terminate the Video thread - this isn't very good because it 
		// might lock up some system resources until next reboot - but the thread is stuck
		if (dwResult == WAIT_TIMEOUT) {
			// This seems to happen if no frames are grabbed for acquisition timeout
			TerminateThread(m_hVideoThread, 0);
		}

		// If the wait didn't fail completely, close the analyzer thread handle
		if (dwResult != WAIT_FAILED) {
			delete m_hVideoThread;
			m_hVideoThread = NULL;
		}
		
		// Wait for analysis and save-to-file threads to stop
		dwResult = WaitForSingleObject(pDoc->m_Results.hAnalyzeStopped, INFINITE);

		m_iRawBufferNum = 0;
	
		for (unsigned int i=0; i < m_iRawBufferSize; i++)
			DeleteCriticalSection((m_csRawVideo+i));

		delete [] m_csRawVideo;
		m_csRawVideo = NULL;

		for (unsigned int i=0; i < m_iAnalyzedBufferSize; i++)	
			DeleteCriticalSection((m_csAnalyzedVideo+i));

		delete [] m_csAnalyzedVideo;
		m_csAnalyzedVideo = NULL;

		m_memDcSourceFrame.SelectObject((CBitmap*)NULL);
		m_bmSourceFrame.DeleteObject();

		m_memDcAnalyzedFrame.SelectObject((CBitmap*)NULL);
		m_bmAnalyzedFrame.DeleteObject();

		m_bData_Loaded = false;

		if (!m_bClosing) {
			InvalidateRect(NULL);
			UpdateWindow();
		}
	}
	return;
}

// Thread function which displays video frames
UINT CVirtualPoleView::VideoThread(LPVOID pParam) {
	CVirtualPoleView *ThisClass = (CVirtualPoleView *)pParam;
	ThisClass->ShowVideoProc(20); // sleep 20 msec between displaying each frame
	//AfxEndThread(0);
	PostQuitMessage(0); // replaces AfxEndThread(0) which seems to crash the App
	return 0;
}

// The actual worker video showing function
void CVirtualPoleView::ShowVideoProc(int RefreshTime) {
	volatile bool *Continue = &m_bAnalyzing;
	
	int DeltaPoints = 1, DeltaPixels = 1;
	CSize ImageSize;
	
	while(*Continue) {
		// Move to the next buffer
		m_iRawBufferNum		 = m_Results.lFrameNumber % m_iRawBufferSize; 
		m_iAnalyzedBufferNum = m_Results.lFrameNumber % m_iAnalyzedBufferSize;

		DeltaPoints = 1;
		DeltaPixels = 1;

		ImageSize = (ResizeVideoAspectRatio(m_imSize,true)).Size();
		m_iNumPoints = (unsigned short)(min(m_Settings.intSettings[9],ImageSize.cx));

		if (m_iNumPoints == ImageSize.cx) {
			// FPS / x_image_length
			DeltaPoints = (int)(floor(1024 / (double)ImageSize.cx));
		} else {
			// x_image_length / FPS
			DeltaPixels = (int)(floor((double)ImageSize.cx / 1024));
		}

		// Create tracking arrays overlaid on image
		if (*(*m_pAnalyze_Params).EnableRealTimeTracking) {

			for (int i= 0; i<1024; i++) {
				// Average position
				m_PointsArray[i].x = (LONG)floor((double)i * ((double)ImageSize.cx/1023));
				m_PointsArray[i].y = ImageSize.cy - long(
					((double)m_Results.MedianArray[i]/(double)ImageSize.cx)
					* ((double)ImageSize.cy/4)
					);

				// Angle
				m_AnglePointsArray[i].x = (LONG)floor((double)i * ((double)ImageSize.cx/1023));
				m_AnglePointsArray[i].y = ImageSize.cy - 
					long((((double)ImageSize.cy/4)/PI) * m_Results.AngleArray[i]);
			}
		}

		// Overlay power spectral density (FFTW) on preview frame
		if (m_Settings.boolSettings[15]) {
			for (int i = 0; i < 256; i++) {
				m_FFTWArray[i].x = (LONG)floor((double)i * ((double)ImageSize.cx/255));
				m_FFTWArray[i].y = (LONG)(ImageSize.cy - m_Results.FFTWAmplitude[i]/20);
			}
		}

		// Redraw the client area
		InvalidateRect(NULL,false);
		UpdateWindow();
		
		Sleep(RefreshTime);
	}
	return;
}

// Display IMAQ error-message in message dialog
void CVirtualPoleView::DisplayImaqError(int error) {
	static Int8 ErrorMessage[256];
    memset(ErrorMessage, 0x00, sizeof(ErrorMessage));
	imgShowError(error,ErrorMessage);
	AfxMessageBox(ErrorMessage, MB_ICONINFORMATION | MB_OK);
}

UINT CVirtualPoleView::ServerThread(LPVOID pParam) {
	CVirtualPoleView *ThisClass = (CVirtualPoleView *)pParam;
	ThisClass->ServerProc();
    return 0;
}

void CVirtualPoleView::ServerProc() {
	volatile bool *Listening = &m_bListening;
	unsigned short port = 20248;

    // WSADATA is a struct that is filled up by the call to WSAStartup
    WSADATA wsaData;

    // The sockaddr_in specifies the address of the socket for TCP/IP sockets.
	// Other protocols use similar structures.
    sockaddr_in local;

    // WSAStartup initializes the program for calling WinSock. The first parameter
	// specifies the highest version of the WinSock specification, the program is allowed to use.
    int wsaret = WSAStartup(0x101,&wsaData);

    // WSAStartup returns zero on success. If it fails we exit.
    if (wsaret != 0)
        return;

    // Now we populate the sockaddr_in structure
    local.sin_family=AF_INET; //Address family
    local.sin_addr.s_addr=INADDR_ANY; //Wild card IP address
    local.sin_port=htons((u_short)port); //port to use

    // The socket function creates our SOCKET
    m_socServer=socket(AF_INET,SOCK_STREAM,0);

    // If the socket() function fails we exit
    if(m_socServer==INVALID_SOCKET)
        return;

    //bind links the socket we just created with the sockaddr_in 
    //structure. Basically it connects the socket with 
    //the local address and a specified port.
    //If it returns non-zero quit, as this indicates error
    if(bind(m_socServer,(sockaddr*)&local,sizeof(local))!=0)
        return;

    //we will need variables to hold the client socket.
    //thus we declare them here.
    SOCKET client;
    sockaddr_in from;
    int fromlen=sizeof(from);

	//listen instructs the socket to listen for incoming 
	//connections from clients. The second arg is the backlog
	if(listen(m_socServer,10)!=0)
       return;
		
	//accept() will accept an incoming client connection
	client=accept(m_socServer,(struct sockaddr*)&from,&fromlen);
	
	// now get things from the client
	int MsgSize;
	char Msg[512];
	bool bListenForSetCmd = false;
	bool bSetSavePath = false;
	bool bSetLogPath = false;
	bool bSetMaxFrames = false;
	while (*Listening) {
		// peek message without destroying queue
		MsgSize = recv(client, Msg, 256, MSG_PEEK);

		// if last byte received is the NULL terminator, then read and destroy the queue
		// if not, we keep the content of the queue for the next iteration of while() loop
		if (Msg[MsgSize-1] == NULL) {
			MsgSize = recv(client, Msg, 256, 0);	
		}

		// Check if input was received and that it is NULL terminated
		if ((MsgSize > 0) && (Msg[MsgSize-1] == NULL)) {
			// Determine if we are listening for SET commands only, or any command
			if (bListenForSetCmd) {
				// Listen for a specific SET command
				//AfxMessageBox("Listens for a SET command...", MB_ICONINFORMATION | MB_OK);
				if (!strcmp(Msg, "VIDEOPATH")) { // Set the video save path
					//AfxMessageBox("set video file path", MB_ICONINFORMATION | MB_OK);
					bSetSavePath = true;
				} else if (!strcmp(Msg, "LOGPATH")) { // Set the maximal no. of frames to save
					AfxMessageBox("set log file path", MB_ICONINFORMATION | MB_OK);
					bSetLogPath = true;
				}else if (!strcmp(Msg, "MAXFRAMES")) { // Set the maximal no. of frames to save
					bSetMaxFrames = true;
					AfxMessageBox("update max frames", MB_ICONINFORMATION | MB_OK);
				} else {
					AfxMessageBox("Invalid SET command received", MB_ICONINFORMATION | MB_OK);
					AfxMessageBox(Msg, MB_ICONINFORMATION | MB_OK);
				}
				bListenForSetCmd = false;
			} else if (bSetSavePath) { // set save path for videos
				m_Settings.Paths[1] = Msg;
				m_dSetProperties.m_csVideoFilePath = Msg;
				bSetSavePath = false;
			} else if (bSetLogPath) { // set save path tracking log files
				m_Settings.Paths[0] = Msg;
				m_dSetProperties.m_csLogFilePath = Msg;
				bSetLogPath = false;
			} else if (bSetMaxFrames) { // set max number of frames to record
				//Msf.Format("%d", m_Settings.intSettings[11]);
				m_Settings.intSettings[11] = atoi(Msg);
				m_dSetProperties.m_bVideoLength = true;
				bSetMaxFrames = false;
			} else {
				// Listen for any command
				if (!strcmp(Msg, "OPENCAM")) { // Open camera
					OnFileStartCaptureVideo();
				} else if (!strcmp(Msg, "CLOSECAM")) // Close camera
					OnFileCloseVideo();
				else if (!strcmp(Msg, "ANALYSE")) // Start real-time analysis
					OnAnalyzeStart();
				else if (!strcmp(Msg, "SAVEVIDEO")) // Start saving video data to disk
					OnAnalyzeSave();
				else if (!strcmp(Msg, "STOP")) // Stop analysis
					OnAnalyzeStop();
				else if (!strcmp(Msg, "SAVELOGON")) // Enable logging of real-time tracking
					m_Settings.boolSettings[2] = true;
				else if (!strcmp(Msg, "SAVELOGOFF")) // Disable logging of real-time tracking
					m_Settings.boolSettings[2] = false;
				else if (!strcmp(Msg, "VIEWFRAMENUMBERON")) // View frame numbers in frame
					m_Settings.boolSettings[10] = true;			
				else if (!strcmp(Msg, "VIEWTRIGGERSQUARESON")) // View trigger squares in frame
					m_Settings.boolSettings[14] = true;			
				else if (!strcmp(Msg, "VIEWTRIGGERSQUARESOFF")) // View trigger squares in frame
					m_Settings.boolSettings[14] = false;			
				else if (!strcmp(Msg, "RESETSOCKET")) { // Reset TCP socket
					OnListen(); // Turn socket OFF
					::Sleep(250);
					OnListen(); // Turn socket ON
				} else if (!strcmp(Msg, "VIEWFRAMENUMBEROFF")) // Turn off frame numbers in frame
					m_Settings.boolSettings[10] = false;
				else if (!strcmp(Msg, "SET")) { // Run a settings command
					//AfxMessageBox("Received a SET command", MB_ICONINFORMATION | MB_OK);
					bListenForSetCmd = true;
				} else {
					// do nothing
					CString csMsg(Msg);
					AfxMessageBox(_T("Invalid command received on listening TCP/IP port:\n\n" + csMsg), MB_ICONINFORMATION | MB_OK);
				}
			}
		}
		Msg[MsgSize] = char("-");
	}

	// Close client socket
	closesocket(client);

    // Closes socket and release socket descriptor
    closesocket(m_socServer);

    //originally this function probably had some use
    //currently this is just for backward compatibility
    //but it is safer to call it as I still believe some
    //implementations use this to terminate use of WS2_32.DLL 
    WSACleanup();

	// Signal that we are not listening anymore
	m_Settings.boolSettings[1] = false;
	m_bListening = false;
}

void CVirtualPoleView::OnUpdateFileStartVideoSession(CCmdUI* pCmdUI) {
	pCmdUI->Enable(!m_bData_Loaded);
}

void CVirtualPoleView::OnListen() {
	m_bListening = !m_bListening;
	if(m_bListening == true)
		AfxBeginThread(ServerThread,this,THREAD_PRIORITY_NORMAL,0,0,NULL);
	else
		closesocket(m_socServer);	
}

void CVirtualPoleView::OnAnalyzeSave() {
	CVirtualPoleDoc* pDoc = GetDocument();
	pDoc->TriggerSave();
}

void CVirtualPoleView::OnUpdateAnalyzeSave(CCmdUI* pCmdUI) {
	CVirtualPoleDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	pCmdUI->Enable(m_bAnalyzing && pDoc->m_Results.bSaveStopped);
}

void CVirtualPoleView::OnUpdateFileListenOnExternalPort(CCmdUI* pCmdUI) {
	pCmdUI->SetCheck(m_bListening);
	pCmdUI->Enable(true);
}

void CVirtualPoleView::onDisableWhenAnalyzing(CCmdUI *pCmdUI) {
	pCmdUI->Enable(m_bData_Loaded && !m_bAnalyzing);
}

// Update the Network Status field in the status bar
void CVirtualPoleView::OnUpdateNetworkStatus(CCmdUI *pCmdUI) {
	if (m_bListening) {
		pCmdUI->SetText(" Socket Open");	
	} else {
		pCmdUI->SetText("Socket Closed");
	}
	pCmdUI->Enable(true);
}

// Update the Save Status field in the status bar
void CVirtualPoleView::OnUpdateSaveStatus(CCmdUI *pCmdUI) {
	if (m_bData_Loaded)	{
		// Get pointer to PoleDoc class
		CVirtualPoleDoc* pDoc = GetDocument();
		ASSERT_VALID(pDoc);

		// Show the number of saved frames
		unsigned long *frame = &pDoc->m_iSavedFrameCounter;
		unsigned long *missedframes = &pDoc->m_iMissedNbBuf;
		if (*frame <= 0) {
			pCmdUI->Enable(false);
		} else {
			CString strPage;

			// Compute RAM buffer usage (%)
			unsigned long nA = pDoc->m_lWrite_Index; // index to disk
			unsigned long nB = pDoc->m_lLoop_Index; // index to memory

			double dUse;
			dUse = 0;
			if (nB > nA)
				dUse = (double(nB - nA) / m_iRawBufferSize) * 100;
			else if (nB < nA)
				dUse = (double(m_iRawBufferSize - (nA - nB)) / m_iRawBufferSize) * 100;

			// Display text in status bar
			strPage.Format( "Saved %d (%d)  RAM Usage: %.0f%%", *frame, (*missedframes+*frame), dUse);
			pCmdUI->SetText( strPage );
		}
		pCmdUI->Enable(true);
	} else {
		pCmdUI->SetText("");
		pCmdUI->Enable(false);
	}
}

// Update FPS (frames/second) field in status bar
void CVirtualPoleView::OnUpdateFPSStatus(CCmdUI *pCmdUI) {
	if (m_bData_Loaded) {
		CString strPage;
		strPage.Format("%d fps", m_Settings.intSettings[9]);
		pCmdUI->SetText(strPage);
		pCmdUI->Enable(true);
	} else {
		pCmdUI->SetText("");
		pCmdUI->Enable(false);
	}
}

// Update Trigger field in status bar
void CVirtualPoleView::OnUpdateTriggerStatus(CCmdUI *pCmdUI) {
	// Check which parameters will be analyzed (virtual contact and/or median whisker position)
	if (m_bData_Loaded) {
		if (m_Settings.boolSettings[13] && !m_Settings.boolSettings[3]) { // both virtual contact AND median position
			pCmdUI->SetText("C+W");
			pCmdUI->Enable(true);
		} else if (!m_Settings.boolSettings[3]) {	// virtual contact only
			pCmdUI->SetText("   C");
			pCmdUI->Enable(true);
		} else {									// nothing will be analyzed
			pCmdUI->Enable(false);
		}
	} else {										// nothing will be analyzed
		pCmdUI->Enable(false);
	}
}

void CVirtualPoleView::RefreshAnalyzeParams() {
	(*m_pAnalyze_Params).Motion_Threshold		= (unsigned char *) &m_Settings.intSettings[6];
	(*m_pAnalyze_Params).Median_Threshold		= (long *) &m_Settings.intSettings[8];
	(*m_pAnalyze_Params).SaveLog				= m_Settings.boolSettings[2];
	(*m_pAnalyze_Params).AnalyzeVideo			= !m_Settings.boolSettings[3];
	(*m_pAnalyze_Params).TriggerOverlays		= m_Settings.boolSettings[14];
	(*m_pAnalyze_Params).DetectMotionInObjects	= &m_Settings.boolSettings[19];
	(*m_pAnalyze_Params).DetectMotion			= &m_Settings.boolSettings[20];
	(*m_pAnalyze_Params).ComputeFFTW			= &m_Settings.boolSettings[15];
	(*m_pAnalyze_Params).EnableFrequencyTrigger = &m_Settings.boolSettings[16];
	(*m_pAnalyze_Params).EnableProximityTrigger = &m_Settings.boolSettings[17];
	(*m_pAnalyze_Params).EnableRealTimeTracking	= &m_Settings.boolSettings[18];
	(*m_pAnalyze_Params).Pole_Threshold			= (double *) &m_Settings.intSettings[7];
	(*m_pAnalyze_Params).FPS					= m_Settings.intSettings[9];
	(*m_pAnalyze_Params).FrameExposure			= m_Settings.intSettings[15];
	(*m_pAnalyze_Params).FreqSNRThreshold		= &m_Settings.intSettings[16];
	(*m_pAnalyze_Params).FrequencyBandpassLow	= &m_Settings.intSettings[17];
	(*m_pAnalyze_Params).FrequencyBandpassHigh	= &m_Settings.intSettings[18];
	//(*m_pAnalyze_Params).VP						= &VPLocation;
	(*m_pAnalyze_Params).VPIndx					= &m_dSetPole.m_idx; // currently selected VP
	(*m_pAnalyze_Params).AllVP					= AllVPLocations;
	(*m_pAnalyze_Params).MotionMask				= &m_crMotionMask;
	(*m_pAnalyze_Params).LogPath				= m_Settings.Paths[0];
	(*m_pAnalyze_Params).CameraMode				= m_Settings.Paths[3];
	(*m_pAnalyze_Params).CameraControlLine		= m_Settings.Paths[4];
	(*m_pAnalyze_Params).CameraTriggerLine		= m_Settings.Paths[5];
	(*m_pAnalyze_Params).CameraTriggerOutLine	= m_Settings.Paths[6];
	(*m_pAnalyze_Params).TrackingOutputCh		= m_Settings.Paths[7];
	(*m_pAnalyze_Params).EventTriggerCh			= m_Settings.Paths[9];
	(*m_pAnalyze_Params).EventTriggerDev		= m_Settings.Paths[10];
	(*m_pAnalyze_Params).IndicatorTriggerINCh	= m_Settings.Paths[11];
	(*m_pAnalyze_Params).VideoPath				= m_Settings.Paths[1];
	(*m_pAnalyze_Params).RawBufferSize.x		= m_iRawBufferSize;
	(*m_pAnalyze_Params).RawBufferSize.y		= m_imSize.cx * m_imSize.cy;
	(*m_pAnalyze_Params).AnalyzedBufferSize.x	= m_iAnalyzedBufferSize;
	(*m_pAnalyze_Params).AnalyzedBufferSize.y	= m_imSize.cx * m_imSize.cy;
	(*m_pAnalyze_Params).LockRawVideo			= m_csRawVideo;
	(*m_pAnalyze_Params).LockAnalyzedVideo		= m_csAnalyzedVideo;
	(*m_pAnalyze_Params).Angle					= m_Settings.MedLineAngle * PI / 180.0;
	
	return;
}

// Make sure Online Help link in menu is enabled
void CVirtualPoleView::OnUpdateHelpOnlineHelp(CCmdUI *pCmdUI) {
	pCmdUI->Enable(true);
}

// Open online help link in default browser
void CVirtualPoleView::OnHelpOnlineHelp() {
	ShellExecute(NULL, "open", "http://code.google.com/p/virtualpole/wiki/VirtualPole", NULL, NULL, SW_SHOWNORMAL);
}


void CVirtualPoleView::OnHelpProjectHomepage() {
	ShellExecute(NULL, "open", "http://code.google.com/p/virtualpole/", NULL, NULL, SW_SHOWNORMAL);
}

void CVirtualPoleView::OnUpdateHelpProjectHomepage(CCmdUI *pCmdUI) {
	pCmdUI->Enable(true);
}

void CVirtualPoleView::OnUpdateHelpReportBug(CCmdUI *pCmdUI) {
	pCmdUI->Enable(true);
}

// Open Report Bug page on GoogleCode in browser
void CVirtualPoleView::OnHelpReportBug() {
	ShellExecute(NULL, "open", "http://code.google.com/p/virtualpole/issues/list", NULL, NULL, SW_SHOWNORMAL);
}

// Show/hide real-time FFT result
void CVirtualPoleView::OnViewRTFFT() {
	m_Settings.boolSettings[15] = !m_Settings.boolSettings[15];
	InvalidateRect(NULL,false);
	UpdateWindow();
}

void CVirtualPoleView::OnUpdateViewRTFFT(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_Settings.boolSettings[15]);
	pCmdUI->Enable(m_bData_Loaded);
}

void CVirtualPoleView::OnViewRTTRACKING()
{
	m_Settings.boolSettings[18] = !m_Settings.boolSettings[18];
	InvalidateRect(NULL,false);
	UpdateWindow();}


void CVirtualPoleView::OnUpdateViewRTTRACKING(CCmdUI *pCmdUI) {
	pCmdUI->SetCheck(m_Settings.boolSettings[18]);
	pCmdUI->Enable(m_bData_Loaded);
}

// Capture current frame as dark frame
void CVirtualPoleView::OnControlCaptureDarkFrame() {
	CVirtualPoleDoc* pDoc = GetDocument(); // document class
	ASSERT_VALID(pDoc);
	RefreshAnalyzeParams();

	// Re-snap from frame grabber
	CRect ROI = CRect(m_Settings.intSettings[4], m_Settings.intSettings[5], m_Settings.intSettings[2]+m_Settings.intSettings[4], m_Settings.intSettings[3]+m_Settings.intSettings[5]);
	m_bData_Loaded = pDoc->OnFileStartCaptureVideo(&ROI, m_pAnalyze_Params);
	m_imSize = pDoc->GetFrameSize();

	pDoc->m_pDarkFrameBuffer = new byte[m_imSize.cx*m_imSize.cy];
	CopyMemory(pDoc->m_pDarkFrameBuffer, pDoc->m_pRawBuffer, m_imSize.cx*m_imSize.cy); // copy current buffer

	pDoc->m_bDarkFrameExists = true;

	// Find max pixel value in dark/background image
	// This value is later added to acquired frames to ensure all values are positive
	// while retaining full dynamic range (ie. we're not loosing pixels darker than
	// this reference image).
	for (long i = 0; i < m_imSize.cx*m_imSize.cy ; i++) {
		pDoc->m_nDarkFrameMaxVal = max(pDoc->m_nDarkFrameMaxVal, pDoc->m_pDarkFrameBuffer[i]);
	}
}

void CVirtualPoleView::OnUpdateControlCaptureDarkFrame(CCmdUI *pCmdUI) {
	CVirtualPoleDoc* pDoc = GetDocument(); // document class
	ASSERT_VALID(pDoc);
	pCmdUI->SetCheck(pDoc->m_bDarkFrameExists);
	pCmdUI->Enable(m_bData_Loaded && pDoc->m_bStopRing);
}

void CVirtualPoleView::OnViewSetMotionMask() {
	// Set new polygonal motion mask for real-time tracking
	m_nMode = 5;
	// Stop analysis
	OnAnalyzeStop();
}
